package com.hunan.api.catv.service.biz.guessing;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.gson.JsonObject;
import com.hunan.api.catv.common.CacheName;
import com.hunan.api.catv.common.Constants;
import com.hunan.api.catv.entity.ServiceResult;
import com.hunan.api.catv.entity.po.GuessingMatchItem;
import com.hunan.api.catv.entity.po.GuessingRanking;
import com.hunan.api.catv.entity.po.GuessingRecord;
import com.hunan.api.catv.entity.po.Member;
import com.hunan.api.catv.entity.vo.GuessingMatchItemVo;
import com.hunan.api.catv.entity.vo.GuessingRankingVo;
import com.hunan.api.catv.service.ResultService;
import com.hunan.api.catv.service.biz.cache.CacheBiz;
import com.hunan.api.catv.service.biz.system.SystemDictBiz;
import com.hunan.api.catv.service.impl.member.MemberService;
import com.hunan.api.catv.utils.GsonUtils;
import com.hunan.api.catv.utils.RedisUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author jiang zhuan
 * @version 1.0
 * @description 描述
 * @date 2021/6/3 0003
 * @package com.hunan.api.catv.service.biz
 * @company 湖南有线
 */
@Service
@CacheConfig(cacheNames = CacheName.guessing)
public class GuessingBiz extends ResultService {

    private static final String TIME_OUT_FORMAT = "HH:mm:ss";
    private static final String DATE_OUT_FORMAT = "yyyy-MM-dd";
    private static final Integer UN_START_STATE = 0;
    private static final Integer IN_START_STATE = 1;
    private static final Integer OVER_START_STATE = 2;
    private static final Integer EUROPEAN_CUP_ACTIVITY_TYPE = 2;
    private static final String RESERVATION_MATCH_CACHE = CacheName.guessing + "::reservationMatch";
    private static final String GUESSING_MATCH_CACHE = CacheName.guessing + "::guessingMatch.";


    @Autowired
    MemberService memberService;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    SystemDictBiz systemDictBiz;

    @Autowired
    private CacheBiz cacheBiz;

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    GuessingBiz guessingBiz;

    @Cacheable(unless = "#result == null", cacheManager = "cacheManager1h")
    public List<GuessingRankingVo> rankingPrizeList() {
       /* List<PrizeStore> prizeStores = new PrizeStore().selectList(new QueryWrapper<PrizeStore>().
                eq(PrizeStore.ACTIVITY_TYPE, 2).
                eq(PrizeStore.DELETED, 1).
                in(PrizeStore.STATE, 2, 3));*/

       /* Map<Integer, List<String>> prizeMaps = prizeStores.stream().collect(Collectors.toMap(PrizeStore::getLockingMemberId,
                e -> {
                    ArrayList<String> list = new ArrayList<>();
                    list.add(e.getName());
                    return list;
                },
                (oldList, newList) -> {
                    oldList.addAll(newList);
                    return oldList;
                }));*/
        PageHelper.startPage(1, 98);
        List<GuessingRanking> guessingRankings = new GuessingRanking().selectList(new QueryWrapper<GuessingRanking>().
                eq(GuessingRanking.ACTIVITY_TYPE, EUROPEAN_CUP_ACTIVITY_TYPE).eq(GuessingRanking.DELETED, 1).orderByDesc(GuessingRanking.WIN_NUM).orderByDesc(GuessingRanking.MAX_NUM).orderByAsc(GuessingRanking.UPDATE_TIME));
        PageInfo<GuessingRanking> guessingRankingPageInfo = new PageInfo<>(guessingRankings);
        List<GuessingRanking> list = guessingRankingPageInfo.getList();

        if (list.size() >= 4) {
            List<GuessingRanking> headList = list.subList(0, 4);
            List<GuessingRanking> tailList = list.subList(4, list.size());
            GuessingRanking guessingRanking = list.get(3);
            PageHelper.startPage(1, 2);
            List<Member> members = new Member().selectList(new QueryWrapper<Member>().eq(Member.MEMBER_TYPE, 0).orderByDesc(Member.MEMBERID));
            PageInfo<Member> memberPageInfo = new PageInfo<>(members);
            List<Member> records = memberPageInfo.getList();
            List<GuessingRanking> midList = new ArrayList<>();
            for (Member record : records) {
                midList.add(GuessingRanking.copyBot(record.getMemberId(), guessingRanking));
            }
            list = Stream.of(headList, midList, tailList).flatMap(Collection::stream).collect(Collectors.toList());
        }

        /*if (list.size() > 0 && list.size() < 100) {
        //排行榜数据不足100 增加机器人冲榜
            PageHelper.startPage(1, 100 - list.size());
            List<Member> members = new Member().selectList(new QueryWrapper<Member>().eq(Member.MEMBER_TYPE, 0).orderByDesc(Member.MEMBERID));
            PageInfo<Member> memberPageInfo = new PageInfo<>(members);
            List<Member> records = memberPageInfo.getList();
            List<GuessingRanking> robotList = records.parallelStream().map(member ->
                    GuessingRanking.initBot(member.getMemberId())
            ).collect(Collectors.toList());
            list.addAll(robotList);
        }*/

        Map<Integer, Member> memberMap = null;
        if (!list.isEmpty()) {
            Set<Integer> memberIds = list.parallelStream().map(GuessingRanking::getMemberId).collect(Collectors.toSet());
            List<Member> members = new Member().selectList(new QueryWrapper<Member>().in(Member.MEMBERID, memberIds));
            memberMap = members.parallelStream().collect(Collectors.toMap(Member::getMemberId, member -> member));
        }
        Map<Integer, Member> finalMemberMap = memberMap;
        List<GuessingRanking> finalList = list;
        List<GuessingRankingVo> rankingVos = list.stream().map(guessingRanking -> {
            GuessingRankingVo guessingRankingVo = new GuessingRankingVo();
            BeanUtil.copyProperties(guessingRanking, guessingRankingVo);
            Member memberInfo = Optional.ofNullable(finalMemberMap.get(guessingRanking.getMemberId())).orElse(new Member());
            guessingRankingVo.setNickName(memberInfo.getNickName());
            guessingRankingVo.setPhone(memberInfo.getPhone() != null ? memberInfo.getPhone().replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2") : "");
            guessingRankingVo.setAvatarUrl(memberInfo.getAvatarUrl());
//            guessingRankingVo.setPrizeNames(prizeMaps.get(guessingRanking.getMemberId()));
            guessingRankingVo.setNumber(finalList.indexOf(guessingRanking) + 1);
            return guessingRankingVo;
        }).collect(Collectors.toList());

        return rankingVos == null ? new ArrayList<>() : rankingVos;
    }

    public GuessingRankingVo myRankingWithDraws(Integer memberId) {
        GuessingRankingVo guessingRankingVo = guessingBiz.myRanking(memberId);
        Integer appLotteryCount = queryMemberDrawsInfo(memberId, Constants.toupiao_conf, "10035");
        guessingRankingVo.setRemainingNumberOfDraws(appLotteryCount == null ? 0 : appLotteryCount);
        return guessingRankingVo;
    }

    @Cacheable(unless = "#result == null", cacheManager = "cacheManager30m")
    public GuessingRankingVo myRanking(Integer memberId) {
        GuessingRankingVo guessingRankingVo = new GuessingRankingVo();
        List<GuessingRanking> guessingRankings = new GuessingRanking().selectList(new QueryWrapper<GuessingRanking>().eq(GuessingRanking.ACTIVITY_TYPE, EUROPEAN_CUP_ACTIVITY_TYPE).eq(GuessingRanking.DELETED, 1).orderByDesc(GuessingRanking.WIN_NUM).orderByDesc(GuessingRanking.MAX_NUM).orderByAsc(GuessingRanking.UPDATE_TIME));

        if (guessingRankings.size() >= 4) {
            List<GuessingRanking> headList = guessingRankings.subList(0, 4);
            List<GuessingRanking> tailList = guessingRankings.subList(4, guessingRankings.size());
            GuessingRanking guessingRanking = guessingRankings.get(3);
            PageHelper.startPage(1, 2);
            List<Member> members = new Member().selectList(new QueryWrapper<Member>().eq(Member.MEMBER_TYPE, 0).orderByDesc(Member.MEMBERID));
            PageInfo<Member> memberPageInfo = new PageInfo<>(members);
            List<Member> records = memberPageInfo.getList();
            List<GuessingRanking> midList = new ArrayList<>();
            for (Member record : records) {
                midList.add(GuessingRanking.copyBot(record.getMemberId(), guessingRanking));
            }
            guessingRankings = Stream.of(headList, midList, tailList).flatMap(Collection::stream).collect(Collectors.toList());
        }
//        guessingRankings = guessingRankings.stream().sorted(Comparator.comparing(GuessingRanking::getWinNum).reversed().thenComparing(GuessingRanking::getMaxNum).reversed().thenComparing(GuessingRanking::getUpdateTime)).collect(Collectors.toList());
        AtomicReference<Integer> number = new AtomicReference<>();
        List<GuessingRanking> finalGuessingRankings = guessingRankings;
        List<GuessingRanking> collect = guessingRankings.parallelStream().filter(guessingRanking -> {
            boolean equals = guessingRanking.getMemberId().equals(memberId);
            if (equals) {
                number.set(finalGuessingRankings.indexOf(guessingRanking));
            }
            return equals;
        }).collect(Collectors.toList());
        if (!collect.isEmpty()) {
            GuessingRanking guessingRanking = collect.get(0);
            BeanUtil.copyProperties(guessingRanking, guessingRankingVo);
            guessingRankingVo.setNumber(number.get() + 1);
            Member member = new Member().selectOne(new QueryWrapper<Member>().eq(Member.MEMBERID, memberId));
            guessingRankingVo.setNickName(member.getNickName());
            guessingRankingVo.setAvatarUrl(member.getAvatarUrl());
            guessingRankingVo.setPhone(member.getPhone() != null ? member.getPhone().replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2") : "");

            /*List<PrizeStore> prizeStores = new PrizeStore().selectList(new QueryWrapper<PrizeStore>().
                    eq(PrizeStore.ACTIVITY_TYPE, 2).
                    eq(PrizeStore.DELETED, 1).
                    eq(PrizeStore.LOCKING_MEMBER_ID, memberId).
                    in(PrizeStore.STATE, 2, 3));*/

           /* Map<Integer, List<String>> prizeMaps = prizeStores.stream().collect(Collectors.toMap(PrizeStore::getLockingMemberId,
                    e -> {
                        ArrayList<String> list = new ArrayList<>();
                        list.add(e.getName());
                        return list;
                    },
                    (oldList, newList) -> {
                        oldList.addAll(newList);
                        return oldList;
                    }));*/
//            guessingRankingVo.setPrizeNames(prizeMaps.get(guessingRanking.getMemberId()));
        } else {
            Member member = new Member().selectOne(new QueryWrapper<Member>().eq(Member.MEMBERID, memberId));
            guessingRankingVo.setNickName(member.getNickName());
            guessingRankingVo.setAvatarUrl(member.getAvatarUrl());
            guessingRankingVo.setPhone(member.getPhone().replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
        }
        return guessingRankingVo;
    }

    private Integer queryMemberDrawsInfo(Integer memberId, String activity, String activityId) {
        JsonObject conf = GsonUtils.fromJson(cacheBiz.getSystemDict(activity), JsonObject.class);
        if (ObjectUtils.isEmpty(conf)) {
            log.warn(activity + " 未配置");
            return null;
        }
        String domain = conf.get("domain").getAsString();
//        String domain = "http://192.168.198.27:8086";
//        String secret = conf.get("secret").getAsString();
        String fromSource = conf.get("fromSource").getAsString();
        Map<String, Object> data = new TreeMap<String, Object>();
        Map<String, Object> paraMap = new HashMap<String, Object>();
//        paraMap.put("activityid", activityId);
//        paraMap.put("fromsource", fromSource);
//        paraMap.put("userid", memberId.toString());
//        paraMap.put("data", data);
//        String sign = Hmacsha256Utils.toupiaoSign(GsonUtils.toJson(data), secret);
//        paraMap.put("sign", sign);
        StringBuffer url = new StringBuffer(domain);
        url.append("/MemberInterface/wnapp_appLotteryCount");
        url.append("?activityid=" + activityId);
        url.append("&fromsource=" + fromSource);
        url.append("&userid=" + memberId.toString());
        HttpHeaders headers = new HttpHeaders();
        try {
            HttpEntity<String> request = new HttpEntity<String>(GsonUtils.toJson(paraMap), headers);
            ResponseEntity<String> response = restTemplate.postForEntity(url.toString(), request, String.class);
            log.info("[queryMemberDrawsInfo: {} {}]", response.getStatusCodeValue(), response.getBody());
            String body = response.getBody();
            JSONObject jsonObject = JSONUtil.parseObj(body);
            return jsonObject.getInt("Data");
        } catch (Exception e) {
            log.error("queryMemberDrawsInfo", e);
            return null;
        }
    }

    public List<GuessingMatchItemVo> matchItemList(Integer memberId) {
        LocalDateTime now = LocalDateTime.now();
        List<GuessingMatchItem> guessingMatchItems = guessingBiz.getGuessingMatchItems();
        Set<String> matchItemIds = null;
        Map<Integer, GuessingRecord> recordMap = null;
        if (memberId != null) {
            List<String> hgetList = (List<String>) redisUtils.hget(RESERVATION_MATCH_CACHE, String.valueOf(memberId));
            if (hgetList != null && !hgetList.isEmpty()) {
                matchItemIds = hgetList.parallelStream().map(s -> s.split("-")[0]).collect(Collectors.toSet());
            }
            List<GuessingRecord> guessingRecords = guessingBiz.getGuessingRecords(memberId);
            recordMap = guessingRecords.parallelStream().collect(Collectors.toMap(GuessingRecord::getMatchItemId, guessingRecord -> guessingRecord, (v1, v2) -> v1));
        }

        Set<String> finalMatchItemIds = matchItemIds;
        Map<Integer, GuessingRecord> finalRecordMap = recordMap;
        List<GuessingMatchItemVo> vos = guessingMatchItems.stream().map(guessingMatchItem -> {
            GuessingMatchItemVo guessingMatchItemVo = new GuessingMatchItemVo();
            BeanUtil.copyProperties(guessingMatchItem, guessingMatchItemVo);
            LocalDate matchStartDate = guessingMatchItem.getMatchStartDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            LocalTime matchStartTime = guessingMatchItem.getMatchStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
            String matchStartDateStr = matchStartDate.format(DateTimeFormatter.ofPattern(DATE_OUT_FORMAT));
            String matchStartTimeStr = matchStartTime.format(DateTimeFormatter.ofPattern(TIME_OUT_FORMAT));
            guessingMatchItemVo.setMatchStartDate(matchStartDateStr);
            guessingMatchItemVo.setMatchStartTime(matchStartTimeStr);
            LocalDateTime matchStartDateTime = LocalDateTime.of(matchStartDate, matchStartTime);
            GuessingRecord guessingRecord = null;
            if (finalRecordMap != null) {
                guessingRecord = finalRecordMap.get(guessingMatchItem.getId());
            }
            guessingMatchItemVo.setMemberGuessingState(guessingRecord == null ? 3 : guessingRecord.getState());
            guessingMatchItemVo.setMemberIsReservation(finalMatchItemIds != null && finalMatchItemIds.contains(String.valueOf(guessingMatchItem.getId())) ? 1 : 0);
            guessingMatchItemVo.setMatchIsStart(now.isAfter(matchStartDateTime) ? 1 : 0);
            LocalDateTime guessingStartTime = guessingMatchItemVo.getGuessingStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            LocalDateTime guessingEndTime = guessingMatchItemVo.getGuessingEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            if (now.isBefore(guessingStartTime)) {
                guessingMatchItemVo.setGuessingState(UN_START_STATE);
            }
            if (now.isAfter(guessingEndTime)) {
                guessingMatchItemVo.setGuessingState(OVER_START_STATE);
            }
            if (now.isAfter(guessingStartTime) && now.isBefore(guessingEndTime)) {
                guessingMatchItemVo.setGuessingState(IN_START_STATE);
            }
            return guessingMatchItemVo;
        }).collect(Collectors.toList());
        return vos;
    }

    @Cacheable(unless = "#result == null", cacheManager = "cacheManager1h")
    public List<GuessingRecord> getGuessingRecords(Integer memberId) {
        return new GuessingRecord().selectList(new QueryWrapper<GuessingRecord>().eq(GuessingRecord.DELETED, 1).eq(GuessingRecord.ACTIVITY_TYPE, 2).eq(GuessingRecord.MEMBER_ID, memberId));
    }


    @Cacheable(unless = "#result == null")
    public List<GuessingMatchItem> getGuessingMatchItems() {
        return new GuessingMatchItem().selectList(new QueryWrapper<GuessingMatchItem>().
                eq(GuessingMatchItem.DELETED, 1).eq(GuessingMatchItem.STATE, 1).
                eq(GuessingMatchItem.ACTIVITY_TYPE, 2).orderByAsc(GuessingMatchItem.MATCH_START_DATE, GuessingMatchItem.MATCH_START_TIME));
    }

    public ServiceResult reservationMatch(Integer matchItemId, Integer memberId) {
        List<String> hgetList = (List<String>) redisUtils.hget(RESERVATION_MATCH_CACHE, String.valueOf(memberId));
        if (ObjectUtils.isEmpty(hgetList)) {
            List<String> list = new ArrayList<>();
            list.add(matchItemId + "-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
            return buildOK(redisUtils.hset(RESERVATION_MATCH_CACHE, String.valueOf(memberId), list));
        }
        List<String> matchItemIds = null;
        if (!hgetList.isEmpty()) {
            matchItemIds = hgetList.stream().map(s -> s.split("-")[0]).collect(Collectors.toList());
            int i = matchItemIds.indexOf(String.valueOf(matchItemId));
            if (i > -1) {
                hgetList.remove(i);
                return buildOK(redisUtils.hset(RESERVATION_MATCH_CACHE, String.valueOf(memberId), hgetList));
            }
        }
        hgetList.add(matchItemId + "-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        return buildOK(redisUtils.hset(RESERVATION_MATCH_CACHE, String.valueOf(memberId), hgetList));
    }

    public ServiceResult guessingMatch(Integer matchItemId, Integer guessingMatchResult, String guessingTeam, Integer memberId) {
        String hget = (String) redisUtils.hget(GUESSING_MATCH_CACHE + matchItemId, String.valueOf(memberId));
        if (StringUtils.isNotBlank(hget)) {
            return buildFail("已完成竞猜，不可更改");
        }
        Date now = new Date();
        GuessingMatchItem guessingMatchItem = new GuessingMatchItem().selectOne(new QueryWrapper<GuessingMatchItem>().eq(GuessingMatchItem.ID, matchItemId));
        if (guessingMatchItem == null) {
            return buildFail("赛事不存在");
        }
        LocalDateTime nowTime = now.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        LocalDateTime guessingStartTime = guessingMatchItem.getGuessingStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        LocalDateTime guessingEndTime = guessingMatchItem.getGuessingEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        if (nowTime.isBefore(guessingStartTime)) {
            return buildFail("未到竞猜时间，请到竞猜时间段参与");
        }
        if (nowTime.isAfter(guessingEndTime)) {
            return buildFail("已过竞猜时间，不能参加此场竞猜，请参与其它场次");
        }
        GuessingRecord guessingRecord = new GuessingRecord();
        guessingRecord.setActivityType(EUROPEAN_CUP_ACTIVITY_TYPE);
        guessingRecord.setGuessingMatchResult(guessingMatchResult);
        guessingRecord.setGuessingTeam(guessingTeam);
        guessingRecord.setGuessingTime(now);
        guessingRecord.setMatchItemId(matchItemId);
        guessingRecord.setMemberId(memberId);
        guessingRecord.setState(0);
        boolean insert = guessingRecord.insert();
        if (insert) {
            redisUtils.del(CacheName.guessing + "::getGuessingRecords." + memberId);
            redisUtils.hset(GUESSING_MATCH_CACHE + matchItemId, String.valueOf(memberId), nowTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:ss:mm")));
            return buildOK(insert, "竞猜成功");
        }
        return buildFail("竞猜失败，请稍后重试");
    }

    public ServiceResult guessingInfo(Integer matchItemId, Integer memberId) {
        List<GuessingRecord> guessingRecords = guessingBiz.getGuessingRecords(memberId);
        GuessingRecord guessingRecord = guessingRecords.parallelStream().filter(guessingRecord1 -> guessingRecord1.getMatchItemId().equals(matchItemId)).findFirst().orElse(null);
        return buildOK(guessingRecord);
    }

}
